﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Autofac;
using IceDog.DNC.Program.WebApi.CustomMiddleware;
using IceDog.DNC.Program.WebApi.Models;
using IceDog.DNC.Program.WebApi.Repositories;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles.Infrastructure;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using NJsonSchema;
using NSwag.AspNetCore;

namespace IceDog.DNC.Program.WebApi
{
    public class Startup
    {
        /// <summary>
        /// StartUp构造函数
        /// </summary>
        /// <param name="configuration">容器注入的配置对象</param>
        public Startup(IConfiguration configuration ,IHostingEnvironment env)
        {
            Configuration = configuration;
            Console.WriteLine($"EnvironmentName : {env.EnvironmentName}");//Production Development
            //自定义config
            /*
            var configBuilder = new ConfigurationBuilder();
            configBuilder.SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();
            Configuration = configBuilder.Build();
            */
        }

        public IConfiguration Configuration { get; }

        /// <summary>
        /// This method gets called by the runtime. Use this method to add services to the container.
        /// 这个方法被运行时调用，使用这个方法来添加服务到DI容器里面
        /// services 本身也是被注入的
        /// </summary>
        /// <param name="services">注入的容器对象</param>

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc()
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
                .AddJsonOptions(c =>
                {
                    //日期格式化的格式(24小时制格式)
                    c.SerializerSettings.DateFormatString = "yyyy/MM/dd HH:mm:ss.ff";
                    //使用默认的格式约定进行序列化和反序列化，即属性大小写不变
                    c.SerializerSettings.ContractResolver = new DefaultContractResolver();
                    //允许格式化异常信息
                    c.AllowInputFormatterExceptionMessages = true;
                    //循环引用处理的方式是报错
                    c.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Error;
                });
            services.AddSingleton<IStringRepository, StringRepository>();
            services.AddTransient<Stopwatch>();
            services.Configure<SiteConfig>(Configuration.GetSection("SiteConfig"));

            var defaultConn = Configuration.GetConnectionString("DefaultConnection");
            var developConnection = Configuration["ConnectionStrings:DevelopConnection"];
            var testSection = Configuration.GetSection("Test");
            var PI = testSection["PI"];
            var hw = testSection["helloWorld"];
            Console.WriteLine($"defaultConn : {defaultConn}");
            Console.WriteLine($"developConnection : {developConnection}");
            Console.WriteLine($"PI : {PI}");
            Console.WriteLine($"hw : {hw}");
        }
        /*
        /// <summary>
        /// 覆盖service container容器为AutoFac,以后研究怎么替换
        /// </summary>
        /// <param name="builder"></param>
        public void ConfigureContainer(ContainerBuilder builder)
        {
            builder.RegisterType<StringRepository>()
                //.AsSelf()
                .As<IStringRepository>()
                .SingleInstance();
        }
        */

        /// <summary>
        /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        /// 这个方法被运行时调用，使用这个方法来配置http请求管道
        /// 顺序 server(kestrel)->pipeline（管道挂载了一个队列的中间件，或者说中间件组成管道）
        /// 中间件在Startup.Configure方法注册,即下面的方法
        /// </summary>
        /// <param name="app">容器注入的参数</param>
        /// <param name="env">容器注入的参数（额外注入）</param>
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            #region 记录错误
            if (env.IsDevelopment())
            {
                //开发环境下，注册开发异常页中间件，放在最开始，用于捕获后面的中间件异常
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseHsts();
                //app.UseExceptionHandler();
            }
            #endregion

            //注册http跳转到https中间件
            app.UseHttpsRedirection();

            //为静态文件和目录浏览提供服务提供支持
            app.UseStaticFiles();

            //测试 app.Run()
            app.Map("/run", AppRunTest);
            //测试app.Use()
            app.Map("/use", AppUseTest);

            //测试app.Map()
            app.Map("/map", AppMapTest);

            //测试app.MapWhen()
            app.Map("/mapwhen", AppMapWhenTest);

            //测试自定义中间件
            app.Map("/custom", AppCustomTest);

            //提供身份验证支持
            //app.UseAuthentication();
            //http响应内容压缩
            //app.UseResponseCompression();
            //提供缓存响应支持
            //app.UseResponseCaching();
            //允许跨域请求
            //app.UseCors();
            //提供用户会话管理
            //app.UseSession();
            //用于重写 Url，并将请求重定向的支持
            //app.UseRewriter();

            //定义和约束请求路由
            //添加一个controller-action的路由

            //app.UseRouter(route =>
            //{
            //    route.DefaultHandler = route.Build();
            //    route.MapRoute("mvcRoute", "mvc/{controller}/{action}");
            //});

            // Register the Swagger generator 注册swagger文档生成中间件

            app.UseSwagger(typeof(Startup).Assembly, settings =>
            {
                settings.PostProcess = document =>
                {
                    document.Info.Version = "v1";
                    document.Info.Title = "ToDo API";
                    document.Info.Description = "A simple ASP.NET Core web API";
                    document.Info.TermsOfService = "None";
                    document.Info.Contact = new NSwag.SwaggerContact
                    {
                        Name = "Shayne Boyer",
                        Email = string.Empty,
                        Url = "https://twitter.com/spboyer"
                    };
                    document.Info.License = new NSwag.SwaggerLicense
                    {
                        Name = "Use under LICX",
                        Url = "https://example.com/license"
                    };
                };
            });

            // Enable the Swagger UI middleware and the Swagger generator 注册 swagger ui 中间件
            app.UseSwaggerUi(typeof(Startup).Assembly, settings =>
            {
                settings.GeneratorSettings.DefaultPropertyNameHandling = PropertyNameHandling.CamelCase;
                settings.DocExpansion = "list";
            });
            //注册mvc中间件
            app.UseMvc();
        }

        private void AppRunTest(IApplicationBuilder app)
        {
            //通过app.Run()进行捕获管道拦截，执行后终止后续管道

            app.Run(async context =>
            {
                await context.Response.WriteAsync("<html><head><meta charset='utf-8'><head><body>app.Run调用，我是委托1，后面还有个委托2，走到我这里就停止了</body></html>");
            });
            app.Run(async context =>
            {
                await context.Response.WriteAsync("<html><head><meta charset='utf-8'><head><body>我是委托2，走不到我这里</body></html>");
            });
        }

        private void AppUseTest(IApplicationBuilder app)
        {
            //通过app.Use()进行捕获管道拦截，执行后通过next.Invoke()执行后续管道，不调用next.Invoke()终止后续管道
            //请求url参数含有next执行下一个，没有则不执行
            //在Configure方法中添加中间件组件的顺序定义了在请求上调用它们的顺序，以及响应的相反顺序。

            //app.Use(next => new SetHeaderTagsMiddleware(next).Invoke);
            app.UseSetHeaderTags("UseSetHeaderTags", "test");
            //app.UseSetHeaderTags(1, 2, 3, 4, 5);//参数无法对应，会报错

            //app.UseSetHeaderTags("a ton of params", "test.", "any", 1);
            ////会报错，给构造函数参数 num赋值默认参数就好了
            //public SetHeaderTagsMiddleware(RequestDelegate next, string identity, string useFor,string name,int num=1) : this(next)
            //app.UseSetHeaderTags("a ton of params", "test.", "any");
            //app.UseSetHeaderTags(true);
            //app.UseSetHeaderTags();
            app.UseStopWatch();
            app.Use(async (context, next) =>
            {
                await context.Response.WriteAsync("<html><head><meta charset='utf-8'><head><body><ul>");
                await context.Response.WriteAsync("<li>1 我是委托A</li>");
                await context.Response.WriteAsync("<li>2 调用委托B之前</li>");
                if (context.Request.Query.ContainsKey("next"))
                {
                    await next.Invoke();
                }
                await context.Response.WriteAsync("<li>3 调用委托B之后</li>");
                await context.Response.WriteAsync("</ul></body></html>");
            });
            app.Run(async context =>
            {
                await context.Response.WriteAsync("<li>4 执行委托B之前</li>");
                await context.Response.WriteAsync("<li>5 我是委托B，执行中。。。</li>");
                await context.Response.WriteAsync("<li>6 执行委托B之后</li>");
            });
        }

        private void AppMapTest(IApplicationBuilder app)
        {
            //访问https://localhost:5001/map/map1跳转到map1分支管道
            //访问https://localhost:5001/map/map2跳转到map2分支管道

            app.Map("/map1", HandleMapTest1);
            app.Map("/map2", HandleMapTest2);
            app.Map("/level1", level1App =>
            {
                level1App.Map("/level2a", level2AApp =>
                {
                    // "/level1/level2a"
                    //...
                    level2AApp.Run(async context =>
                    {
                        await context.Response.WriteAsync("Hello from level1/level2a.");
                    });
                });
                level1App.Map("/level2b", level2BApp =>
                {
                    // "/level1/level2b"
                    //...
                    level2BApp.Run(async context =>
                    {
                        await context.Response.WriteAsync("Hello from level1/level2b.");
                    });
                });

                level1App.Run(async context =>
                {
                    await context.Response.WriteAsync("Hello from level1.");
                });
            });
            app.Map("/range1/range2A", range2A =>
            {
                range2A.Run(async context =>
                {
                    await context.Response.WriteAsync("Hello from range1/range2A.");
                });
            });
            app.Run(async context =>
            {
                await context.Response.WriteAsync("Hello from non-Map delegate.");
            });
        }
        private void HandleMapTest1(IApplicationBuilder app)
        {
            app.Run(async context =>
            {
                await context.Response.WriteAsync("Map Test 1");
            });
        }

        private void HandleMapTest2(IApplicationBuilder app)
        {
            app.Run(async context =>
            {
                await context.Response.WriteAsync("Map Test 2");
            });
        }

        private void AppMapWhenTest(IApplicationBuilder app)
        {
            //访问https://localhost:5001/mapwhen?branch=1跳转到url含有key branch 情况的分支管道
            app.MapWhen(context => context.Request.Query.ContainsKey("branch"), HandleBranch);

            app.Run(async context =>
            {
                await context.Response.WriteAsync("Hello from non-Map delegate.");
            });
        }

        private void HandleBranch(IApplicationBuilder app)
        {
            app.Run(async context =>
            {
                var branchVer = context.Request.Query["branch"];
                await context.Response.WriteAsync($"Branch used = {branchVer}");
            });
        }

        private void AppCustomTest(IApplicationBuilder app)
        {
            //访问https://localhost:5001/custom?culture=en-US
            //访问https://localhost:5001/custom?culture=zh-CHT
            /*
            app.Use((context, next) =>
            {
                var cultureQuery = context.Request.Query["culture"];
                if (string.IsNullOrWhiteSpace(cultureQuery)) return next();
                var culture = new CultureInfo(cultureQuery);

                CultureInfo.CurrentCulture = culture;
                CultureInfo.CurrentUICulture = culture;

                // Call the next delegate/middleware in the pipeline
                return next();
            });
            */
            //实现了和上面注释部分等价的中间件
            app.UseRequestCulture();
            app.Run(async context =>
            {
                await context.Response.WriteAsync("<html><head><meta charset='utf-8'><head><body>");
                await context.Response.WriteAsync(
                    $"Hello {CultureInfo.CurrentCulture.DisplayName}");
                await context.Response.WriteAsync("</body></html>");
            });

        }
    }
}
